<?php

/**
 * Description of Persistencia
 *
 * @author Cristiana Sasse
 */
abstract class Persistencia {
    /** @var RelacionamentoPersistencia[] */
    private $relacionamento = array();
    private $esquema;
    private $tabela;
    /** @var Model */
    protected $Model;
    /** @var Principal */
    protected $Principal;    

    public function __construct(Model $Model = null) {
        $this->setRelacionamento();
        if ($Model) {
            $this->setModel($Model);
        }
        $this->Principal = Principal::getInstance();        
    }
    
    public function getModel() {
        return $this->Model;
    }

    public function setModel(Model $Model) {
        $this->Model = $Model;
    }

    abstract protected function setRelacionamento();

    protected function addRelacionamento($nomeBanco, $nomeModel, $chave=false, $autoIncrement=false) {
        $this->relacionamento[] = new RelacionamentoPersistencia($nomeBanco, $nomeModel, $chave, $autoIncrement);
    }
    
    public function getRelacionamento() {
        return $this->relacionamento;
    }

    public function getEsquema() {
        return $this->esquema;
    }

    public function setEsquema($esquema) {
        $this->esquema = $esquema;
    }

    public function getTabela($comEsquema=false) {
        if ($comEsquema && $this->getEsquema()) {
            return $this->getEsquema().'.'.$this->tabela;
        }
        return $this->tabela;
    }
    
    public function setTabela($tabela) {
        $this->tabela = $tabela;
    }
    
    protected function getCondicaoChaves(Query $oQuery) {
        $aCondicao = array();
        foreach ($this->relacionamento as $oRelacionamento) {
            if ($oRelacionamento->isChave()) {
                $sValor = callGetter($this->Model, $oRelacionamento->getNomeModel());
                $aCondicao[] = $oRelacionamento->getNomeBanco().' = '.$oQuery->escapeString($sValor);
            }
        }
        return $aCondicao;
    }

    public function buscaDados() {
        $oQuery  = $this->newQuery();
        $aWhere  = $this->getCondicaoChaves($oQuery);
        $sSelect = "select * 
                      from ".$this->getTabela(true)." 
                     where ".implode(' and ', $aWhere);        
        $oQuery->setSql($sSelect);
        $oQuery->execute();
        if ($oRes = $oQuery->fetchObject()) {
            $this->persisteObjetoFromFetch($oRes);
            return true;
        }
        return false;
    }
    
    protected function persisteObjetoFromFetch(stdClass $oRes, Model $oModel = null) {
        if (!$oModel) {
            $oModel = $this->getModel();
        }
        foreach ($this->relacionamento as $oRelacionamento) {
            if (isset($oRes->{$oRelacionamento->getNomeBanco()})) {
                callSetter($oModel, $oRelacionamento->getNomeModel(), $oRes->{$oRelacionamento->getNomeBanco()});
            }
        }        
    }
    
    public function insere() {
        $oQuery  = $this->newQuery();
        $aCampo  = array();
        $aValor  = array();
        foreach ($this->relacionamento as $oRelacionamento) {            
            $aCampo[] = $oRelacionamento->getNomeBanco();
            $sValor = callGetter($this->Model, $oRelacionamento->getNomeModel());
            if ($oRelacionamento->getAutoIncrement() && !$sValor) {
                $sValor = $this->getNextAutoIncrement();
                callSetter($this->Model, $oRelacionamento->getNomeModel(), $sValor);
            }
            $aValor[] = $oQuery->escapeString($sValor);
        }
        $sInsert = "insert into ".$this->getTabela(true)."(
                           ".implode(', ', $aCampo)."
                         ) values (
                           ".implode(', ', $aValor)."
                         )";
        $oQuery->setSql($sInsert);
        return $oQuery->execute();
    }
    
    public function altera() {
        $oQuery  = $this->newQuery();
        $aWhere  = $this->getCondicaoChaves($oQuery);
        $aCampo  = array();
        foreach ($this->relacionamento as $oRelacionamento) {
            if (!$oRelacionamento->isChave()) {
                $sValor = callGetter($this->Model, $oRelacionamento->getNomeModel());                
                $aCampo[] = $oRelacionamento->getNomeBanco().' = '.$oQuery->escapeString($sValor);
            }
        }
        $sUpdate = "update ".$this->getTabela(true)." set
                           ".implode(', ', $aCampo)."
                     where ".implode(' and ', $aWhere);
        $oQuery->setSql($sUpdate);
        return $oQuery->execute();        
    }
    
    public function exclui() {
        $oQuery  = $this->newQuery();
        $aWhere  = $this->getCondicaoChaves($oQuery);
        $sDelete = "delete from ".$this->getTabela(true)."
                     where ".implode(' and ', $aWhere);
        $oQuery->setSql($sDelete);
        return $oQuery->execute();          
    }
    
    public function getSqlPadraoConsulta() {
        $sSelect = "select * from ".$this->getTabela(true)." ";
        return $sSelect;
    }
    
    public function newQuery($sql = '') {
        return $this->Principal->getConexao()->newQuery($sql);
    }
    
    public function getNextAutoIncrement() {
        $oQuery  = $this->newQuery();        
        $sCampoAutoIncrement = null;
        $aCondicao = array();
        foreach ($this->relacionamento as $oRelacionamento) {
            if ($oRelacionamento->getAutoIncrement()) {
                $sCampoAutoIncrement = $oRelacionamento->getNomeBanco();
            }
            else if ($oRelacionamento->isChave()) {
                $sValor = callGetter($this->Model, $oRelacionamento->getNomeModel());
                $aCondicao[] = $oRelacionamento->getNomeBanco().' = '.$oQuery->escapeString($sValor);
            }            
        }        
        if ($sCampoAutoIncrement) {
            $sSelect = "select coalesce(max(".$sCampoAutoIncrement."), 0) + 1 as proximo 
                          from ".$this->getTabela(true);
            if ($aCondicao) {
                $sSelect .= " where ".implode(' and ', $aCondicao);
            }
            $oQuery->setSql($sSelect);
            $oQuery->execute();
            if ($oRes = $oQuery->fetchObject()) {
                return $oRes->proximo;
            }
        }
        return null;
    }

}

